/**
 * @file WebUI服务入口文件
 */

import express from 'express';
import type { WebUiConfigType } from './src/types';
import { createServer } from 'http';
import { randomUUID } from 'node:crypto';
import { createServer as createHttpsServer } from 'https';
import { NapCatPathWrapper } from 'napcat-common/src/path';
import { WebUiConfigWrapper } from '@/napcat-webui-backend/src/helper/config';
import { ALLRouter } from '@/napcat-webui-backend/src/router';
import { cors } from '@/napcat-webui-backend/src/middleware/cors';
import { createUrl, getRandomToken } from '@/napcat-webui-backend/src/utils/url';
import { sendError } from '@/napcat-webui-backend/src/utils/response';
import { join } from 'node:path';
import { terminalManager } from '@/napcat-webui-backend/src/terminal/terminal_manager';
import multer from 'multer';
import * as net from 'node:net';
import { WebUiDataRuntime } from './src/helper/Data';
import { existsSync, readFileSync } from 'node:fs'; // 引入multer用于错误捕获
import { ILogWrapper } from 'napcat-common/src/log-interface';
import { ISubscription } from 'napcat-common/src/subscription-interface';
import { IStatusHelperSubscription } from '@/napcat-common/src/status-interface';
// 实例化Express
const app = express();
/**
 * 初始化并启动WebUI服务。
 * 该函数配置了Express服务器以支持JSON解析和静态文件服务，并监听6099端口。
 * 无需参数。
 * @returns {Promise<void>} 无返回值。
 */
export let WebUiConfig: WebUiConfigWrapper;
export let webUiPathWrapper: NapCatPathWrapper;
export let logSubscription: ISubscription;
export let statusHelperSubscription: IStatusHelperSubscription;
const MAX_PORT_TRY = 100;

export let webUiRuntimePort = 6099;
// 全局变量：存储需要在QQ登录成功后发送的新token
export let pendingTokenToSend: string | null = null;

/**
 * 存储WebUI启动时的初始token，用于鉴权
 * - 无论是否在运行时修改密码，都应该使用此token进行鉴权
 * - 运行时手动修改的密码将会在下次napcat重启后生效
 * - 如果需要在运行时修改密码并立即生效，则需要在前端调用路由进行修改
 */
let initialWebUiToken: string = '';

export function setInitialWebUiToken (token: string) {
  initialWebUiToken = token;
}

export function getInitialWebUiToken (): string {
  return initialWebUiToken;
}

export function setPendingTokenToSend (token: string | null) {
  pendingTokenToSend = token;
}

export async function InitPort (parsedConfig: WebUiConfigType): Promise<[string, number, string]> {
  try {
    await tryUseHost(parsedConfig.host);
    const port = await tryUsePort(parsedConfig.port, parsedConfig.host);
    return [parsedConfig.host, port, parsedConfig.token];
  } catch (error) {
    console.log('host或port不可用', error);
    return ['', 0, randomUUID()];
  }
}

async function checkCertificates (logger: ILogWrapper): Promise<{ key: string, cert: string; } | null> {
  try {
    const certPath = join(webUiPathWrapper.configPath, 'cert.pem');
    const keyPath = join(webUiPathWrapper.configPath, 'key.pem');

    if (existsSync(certPath) && existsSync(keyPath)) {
      const cert = readFileSync(certPath, 'utf8');
      const key = readFileSync(keyPath, 'utf8');
      logger.log('[NapCat] [WebUi] 找到SSL证书，将启用HTTPS模式');
      return { cert, key };
    }
    return null;
  } catch (error) {
    logger.log('[NapCat] [WebUi] 检查SSL证书时出错: ' + error);
    return null;
  }
}
export async function InitWebUi (logger: ILogWrapper, pathWrapper: NapCatPathWrapper, Subscription: ISubscription, statusSubscription: IStatusHelperSubscription) {
  webUiPathWrapper = pathWrapper;
  logSubscription = Subscription;
  statusHelperSubscription = statusSubscription;
  WebUiConfig = new WebUiConfigWrapper();
  let config = await WebUiConfig.GetWebUIConfig();

  // 检查是否禁用WebUI（若禁用则不进行密码检测）
  if (config.disableWebUI) {
    logger.log('[NapCat] [WebUi] WebUI is disabled by configuration.');
    return;
  }

  // 检查并更新默认密码（仅在启用WebUI时）
  if (config.token === 'napcat' || !config.token) {
    const randomToken = process.env['NAPCAT_WEBUI_SECRET_KEY'] || getRandomToken(8);
    await WebUiConfig.UpdateWebUIConfig({ token: randomToken });
    logger.log('[NapCat] [WebUi] 检测到默认密码，已自动更新为安全密码');

    // 存储token到全局变量，等待QQ登录成功后发送
    setPendingTokenToSend(randomToken);
    logger.log('[NapCat] [WebUi] 新密码将在QQ登录成功后发送给用户');

    // 重新获取更新后的配置
    config = await WebUiConfig.GetWebUIConfig();
  }

  // 存储启动时的初始token用于鉴权
  setInitialWebUiToken(config.token);

  const [host, port, token] = await InitPort(config);
  webUiRuntimePort = port;
  if (port === 0) {
    logger.log('[NapCat] [WebUi] Current WebUi is not run.');
    return;
  }
  WebUiDataRuntime.setWebUiConfigQuickFunction(
    async () => {
      const autoLoginAccount = process.env['NAPCAT_QUICK_ACCOUNT'] || WebUiConfig.getAutoLoginAccount();
      if (autoLoginAccount) {
        try {
          const { result, message } = await WebUiDataRuntime.requestQuickLogin(autoLoginAccount);
          if (!result) {
            throw new Error(message);
          }
          console.log(`[NapCat] [WebUi] Auto login account: ${autoLoginAccount}`);
        } catch (error) {
          console.log('[NapCat] [WebUi] Auto login account failed.' + error);
        }
      }
    });
  // ------------注册中间件------------
  // 使用express的json中间件
  app.use(express.json());

  // CORS中间件
  // TODO:
  app.use(cors);

  // 如果是webui字体文件，挂载字体文件
  app.use('/webui/fonts/AaCute.woff', async (_req, res, next) => {
    const isFontExist = await WebUiConfig.CheckWebUIFontExist();
    if (isFontExist) {
      res.sendFile(WebUiConfig.GetWebUIFontPath());
    } else {
      next();
    }
  });

  // 如果是自定义色彩，构建一个css文件
  app.use('/files/theme.css', async (_req, res) => {
    const colors = await WebUiConfig.GetTheme();

    let css = ':root, .light, [data-theme="light"] {';
    for (const key in colors.light) {
      css += `${key}: ${colors.light[key]};`;
    }
    css += '}';
    css += '.dark, [data-theme="dark"] {';
    for (const key in colors.dark) {
      css += `${key}: ${colors.dark[key]};`;
    }
    css += '}';

    res.send(css);
  });

  // ------------中间件结束------------

  // ------------挂载路由------------
  // 挂载静态路由（前端），路径为 /webui
  app.use('/webui', express.static(pathWrapper.staticPath, {
    maxAge: '1d',
  }));
  // 初始化WebSocket服务器
  const sslCerts = await checkCertificates(logger);
  const isHttps = !!sslCerts;
  const server = isHttps && sslCerts ? createHttpsServer(sslCerts, app) : createServer(app);
  server.on('upgrade', (request, socket, head) => {
    terminalManager.initialize(request, socket, head, logger);
  });
  // 挂载API接口
  app.use('/api', ALLRouter);
  // 所有剩下的请求都转到静态页面
  const indexFile = join(pathWrapper.staticPath, 'index.html');

  app.all(/\/webui\/(.*)/, (_req, res) => {
    res.sendFile(indexFile);
  });

  // 初始服务（先放个首页）
  app.all('/', (_req, res) => {
    res.status(301).header('Location', '/webui').send();
  });

  // 错误处理中间件，捕获multer的错误
  app.use((err: Error, _: express.Request, res: express.Response, next: express.NextFunction) => {
    if (err instanceof multer.MulterError) {
      return sendError(res, err.message, true);
    }
    next(err);
  });

  // 全局错误处理中间件（非multer错误）
  app.use((_: Error, __: express.Request, res: express.Response, ___: express.NextFunction) => {
    sendError(res, 'An unknown error occurred.', true);
  });

  // ------------启动服务------------
  server.listen(port, host, async () => {
    const searchParams = { token };
    logger.log(`[NapCat] [WebUi] WebUi Token: ${token}`);
    logger.log(
      `[NapCat] [WebUi] WebUi User Panel Url: ${createUrl('127.0.0.1', port.toString(), '/webui', searchParams)}`
    );
    if (host !== '') {
      logger.log(
        `[NapCat] [WebUi] WebUi User Panel Url: ${createUrl(host, port.toString(), '/webui', searchParams)}`
      );
    }
  });

  // ------------Over！------------
}

async function tryUseHost (host: string): Promise<string> {
  return new Promise((resolve, reject) => {
    try {
      const server = net.createServer();
      server.on('listening', () => {
        server.close();
        resolve(host);
      });

      server.on('error', (err: any) => {
        if (err.code === 'EADDRNOTAVAIL') {
          reject(new Error('主机地址验证失败，可能为非本机地址'));
        } else {
          reject(new Error(`遇到错误: ${err.code}`));
        }
      });

      // 尝试监听 让系统随机分配一个端口
      server.listen(0, host);
    } catch (error) {
      // 这里捕获到的错误应该是启动服务器时的同步错误
      reject(new Error(`服务器启动时发生错误: ${error}`));
    }
  });
}

async function tryUsePort (port: number, host: string, tryCount: number = 0): Promise<number> {
  return new Promise((resolve, reject) => {
    try {
      const server = net.createServer();
      server.on('listening', () => {
        server.close();
        resolve(port);
      });

      server.on('error', (err: any) => {
        if (err.code === 'EADDRINUSE') {
          if (tryCount < MAX_PORT_TRY) {
            // 使用循环代替递归
            resolve(tryUsePort(port + 1, host, tryCount + 1));
          } else {
            reject(new Error(`端口尝试失败，达到最大尝试次数: ${MAX_PORT_TRY}`));
          }
        } else {
          reject(new Error(`遇到错误: ${err.code}`));
        }
      });

      // 尝试监听端口
      server.listen(port, host);
    } catch (error) {
      // 这里捕获到的错误应该是启动服务器时的同步错误
      reject(new Error(`服务器启动时发生错误: ${error}`));
    }
  });
}
